package com.fast.fast.common.core.exception;

import cn.dev33.satoken.exception.DisableLoginException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import com.fast.fast.common.base.constant.enums.HttpStatusEnum;
import com.fast.fast.common.base.exception.BizException;
import com.fast.fast.common.base.vo.Result;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.servlet.UnavailableException;
import java.io.IOException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.List;


/**
 * 全局异常处理器类
 *
 * @author lyf
 * @date 2022/01/01 00:00 周六
 **/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 获取yml中配置的上传文件的大小
     */
    @Value("${spring.servlet.multipart.max-file-size:10MB}")
    private String maxFileSize;

    /**
     * 业务异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(BizException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<?> bizException(BizException e) {
        log.error("业务异常:{}", e.getMessage(), e);
        return Result.error("业务异常:" + e.getMessage(), e.getCode() == null ? Result.error().getCode() : e.getCode());
    }

    /**
     * 空指针异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(NullPointerException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<?> nullPointerException(NullPointerException e) {
        log.error("空指针异常:{}", e.getMessage(), e);
        return Result.error("空指针异常");
    }

    /**
     * IO异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(IOException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<?> ioException(IOException e) {
        log.error("IO异常:{}", e.getMessage(), e);
        return Result.error("IO异常");
    }

    /**
     * 非法参数异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<?> illegalArgumentException(IllegalArgumentException e) {
        log.error("非法参数异常:{}", e.getMessage(), e);
        return Result.error("非法参数异常", HttpStatusEnum.BAD_REQUEST.getCode());
    }

    /**
     * content-type为application/json时,请求体为空的异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<?> httpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error("400异常:{}", HttpStatusEnum.REQUEST_BODY_IS_MISSING.getMsg(), e);
        return Result.error(HttpStatusEnum.REQUEST_BODY_IS_MISSING.getMsg(), HttpStatusEnum.REQUEST_BODY_IS_MISSING.getCode());
    }

    /**
     * 请求参数校验失败异常(以form-data形式传参时)
     *
     * @param e
     * @return
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<?> bindExceptionHandler(BindException e) {
        return getRequestParamsException(e);
    }

    private Result<?> getRequestParamsException(BindException e) {
        StringBuilder sb = new StringBuilder("请求参数校验失败.");
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (int i = 0; i < fieldErrors.size(); i++) {
            sb
                    .append(fieldErrors.get(i).getField())
                    .append(":")
                    .append(fieldErrors.get(i).getDefaultMessage());
            if (i != fieldErrors.size() - 1) {
                sb.append(", ");
            }
        }
        String msg = sb.toString();
        log.error("400异常:{}", msg, e);
        return Result.error(msg, HttpStatusEnum.BAD_REQUEST.getCode());
    }

    /**
     * 请求参数校验失败异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<?> methodArgumentNotValidException(MethodArgumentNotValidException e) {
        return getRequestParamsException(e);
    }

    /**
     * 415异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    public Result<?> httpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        log.error("415异常:{}", HttpStatusEnum.UNSUPPORTED_MEDIA_TYPE.getMsg(), e);
        return Result.error(HttpStatusEnum.UNSUPPORTED_MEDIA_TYPE.getMsg(), HttpStatusEnum.UNSUPPORTED_MEDIA_TYPE.getCode());
    }

    /**
     * 503异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(UnavailableException.class)
    @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
    public Result<?> unavailableException(UnavailableException e) {
        log.error("503异常:{}", HttpStatusEnum.SERVICE_UNAVAILABLE.getMsg(), e);
        return Result.error(HttpStatusEnum.SERVICE_UNAVAILABLE.getMsg(), HttpStatusEnum.SERVICE_UNAVAILABLE.getCode());
    }

    /**
     * 上传的文件超出设定容量异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<?> maxUploadSizeExceededException(MaxUploadSizeExceededException e) {
        String msg = String.format("上传的文件超出设定容量:%s,请降低文件大小", maxFileSize);
        log.error(msg, e);
        return Result.error(msg);
    }


    /**
     * 远程接口调用失败异常
     *
     * @param e
     * @return
     */
    public static final String SERVICE_UNAVAILABLE_MSG_1 = HttpStatusEnum.SERVICE_UNAVAILABLE.getCode().toString();
    public static final String SERVICE_UNAVAILABLE_MSG_2 = "Failed to connect to";
    public static final String SERVICE_UNAVAILABLE_MSG_RETURN = "远程接口调用失败";

    @ExceptionHandler(FeignException.class)
    @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
    public Result<?> feignException(FeignException e) {
        String msg = e.getMessage();
        log.error(msg, e);
        if (msg.contains(SERVICE_UNAVAILABLE_MSG_1) || msg.contains(SERVICE_UNAVAILABLE_MSG_2)) {
            return Result.error(SERVICE_UNAVAILABLE_MSG_RETURN, HttpStatusEnum.SERVICE_UNAVAILABLE.getCode());
        }
        return Result.error(msg, HttpStatusEnum.SERVICE_UNAVAILABLE.getCode());
    }


    /**
     * sql唯一索引异常
     *
     * @param e
     * @return
     */
    public static final String DATA_DUPLICATION_MSG = "Duplicate entry";
    public static final String DATA_DUPLICATION_RETURN = "数据重复";

    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<?> sqlIntegrityConstraintViolationException(SQLIntegrityConstraintViolationException e) {
        String msg = e.getMessage();
        log.error(msg, e);
        if (msg.contains(DATA_DUPLICATION_MSG)) {
            return Result.error(DATA_DUPLICATION_RETURN);
        }
        return Result.error(msg);
    }

    /**
     * Sa-Token的401异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(NotLoginException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public Result<?> notLoginException(NotLoginException e) {
        log.error("用户未登录", e);
        return Result.error("用户未登录", HttpStatusEnum.UNAUTHENTICATED.getCode());
    }

    /**
     * Sa-Token的403异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(NotPermissionException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public Result<?> notPermissionException(NotPermissionException e) {
        log.error(e.getMessage(), e);
        return Result.error(e.getMessage(), HttpStatusEnum.FORBIDDEN.getCode());
    }

    /**
     * Sa-Token的403异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(NotRoleException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public Result<?> notRoleException(NotRoleException e) {
        log.error(e.getMessage(), e);
        return Result.error(e.getMessage(), HttpStatusEnum.FORBIDDEN.getCode());
    }

    /**
     * Sa-Token的账号被封禁异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(DisableLoginException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public Result<?> disableLoginException(DisableLoginException e) {
        String msg = e.getMessage() + "," + e.getDisableTime() + "秒后解封";
        log.error(msg, e);
        return Result.error(msg, HttpStatusEnum.FORBIDDEN.getCode());
    }
}
